/*
  Copyright (c) 2010
  The Regents of the University of Michigan
  All Rights Reserved

  Permission is granted to use, copy, create derivative works, and
  redistribute this software and such derivative works for any purpose,
  so long as the name of the University of Michigan is not used in
  any advertising or publicity pertaining to the use or distribution
  of this software without specific, written prior authorization. If
  the above copyright notice or any other identification of the
  University of Michigan is included in any copy of any portion of
  this software, then the disclaimer below must also be included.

  This software is provided as is, without representation or warranty
  of any kind either express or implied, including without limitation
  the implied warranties of merchantability, fitness for a particular
  purpose, or noninfringement.  The Regents of the University of
  Michigan shall not be liable for any damages, including special,
  indirect, incidental, or consequential damages, with respect to any
  claim arising out of or in connection with the use of the software,
  even if it has been or is hereafter advised of the possibility of
  such damages.
*/

#include <Python.h>
#include <inttypes.h>
#include <stdio.h>

#include "contact.h"
#include "id.h"



static PyObject *
compute_node_id(PyObject *self, PyObject *args) {
  const char *ip;
  struct ContactAddress contact = {0};
  if (!PyArg_ParseTuple(args, "sH", &ip, &contact.port)) {
    return NULL;
  }

  if (!inet_aton(ip, &contact.addr)) {
    PyErr_SetString(PyExc_ValueError, "invalid IP address!");
    return NULL;
  }

  char *id = clearview_get_id(&contact);
  PyObject *res = Py_BuildValue("s#", id, KEY_SIZE);
  clearview_id_free(id);

  return res;
}


static PyObject *
compute_shared_prefix_bits(PyObject *self, PyObject *args) {
  const char *id1, *id2;
  int len1, len2;

  if (!PyArg_ParseTuple(args, "s#s#", &id1, &len1, &id2, &len2)) {
    return NULL;
  }

  if (len1 != KEY_SIZE || len2 != KEY_SIZE) {
    PyErr_SetString(PyExc_ValueError, "require ids of appropriate size!");
    return NULL;
  }

  gint ret = clearview_id_shared_prefix_bits((const unsigned char *)id1,
                                             (const unsigned char *)id2);
  return PyInt_FromLong(ret);
}

static PyObject *
compute_id_distance(PyObject *self, PyObject *args) {
  const char *id1, *id2;
  int len1, len2;

  if (!PyArg_ParseTuple(args, "s#s#", &id1, &len1, &id2, &len2)) {
    return NULL;
  }

  if (len1 != 20 || len2 != 20) {
    PyErr_SetString(PyExc_ValueError, "require ids of length 20!");
    return NULL;
  }

  char *dist = get_id_distance(id1, id2);
  PyObject *res = Py_BuildValue("s#", dist, KEY_SIZE);
  clearview_id_free(dist);

  return res;
}


static PyObject *
brute_force_closest_id(PyObject *self, PyObject *args) {
  const char *target_id;
  int target_id_len;
  PyObject *available_ids;

  if (!PyArg_ParseTuple(args, "s#O!", &target_id, &target_id_len,
                        &PyTuple_Type, &available_ids)) {
    return NULL;
  }

  Py_ssize_t len = PyTuple_Size(available_ids);
  if (!len) {
    Py_RETURN_NONE;
  }

  PyObject **items = PySequence_Fast_ITEMS(available_ids);
  Py_ssize_t ii, best = len;
  int max_shared_bits = -1;

  for (ii = 0; ii != len; ++ii) {
    Py_ssize_t cand_len;
    char *cand;
    
    if (PyString_AsStringAndSize(items[ii], &cand, &cand_len) == -1) {
        return NULL;
    }

    if (cand_len != KEY_SIZE) {
      PyErr_SetString(PyExc_ValueError, "require ids of appropriate size!");
      return NULL;
    }

    int cand_shared_bits = clearview_id_shared_prefix_bits(target_id, cand);
    if (cand_shared_bits > max_shared_bits) {
      max_shared_bits = cand_shared_bits;
      best = ii;
    }
  }

  return PyInt_FromSsize_t(best);
}


static PyMethodDef cVuze_UtilMethods[] = {
  {"compute_node_id", compute_node_id, METH_VARARGS,
   "Compute the Vuze ID of the given (ip, port) pair."},
  {"compute_shared_prefix_bits", compute_shared_prefix_bits, METH_VARARGS,
   "Compute the number of prefix bits shared by the two passed Vuze IDs."},
  {"compute_id_distance", compute_id_distance, METH_VARARGS,
   "Compute the distance between the two passed Vuze IDs."},
  {"brute_force_closest_id", brute_force_closest_id, METH_VARARGS,
   "Return the index of the closest available ID to the target ID."},
  { NULL, NULL, 0, NULL}
};


PyMODINIT_FUNC
initcvuze_util(void) {
  (void) Py_InitModule("cvuze_util", cVuze_UtilMethods);
}
